<?php


namespace app\utils;


class Arr
{
    /**
     * 数组层级缩进转换
     * @param array $array 源数组
     * @param string $field 父级id字段名
     * @param int $pid 父级id值
     * @param int $level 层级
     * @param array $list
     * @return array
     */
    public static function arrayToLevel(array $array, string $field = 'pid', int $pid = 0, int $level = 1, array &$list = []): array
    {
        if (empty($array)) {
            return [];
        }
        $pid_arr = array_column($array, $field);// 所有有子元素的父级id集合
        if ($pid === 0 && !in_array($pid, $pid_arr)) {
            $pid = min($pid_arr);// 首次循环时没有根pid时，赋予一个最小的为pid
        }
        foreach ($array as $v) {
            if ($v[$field] == $pid) {
                $v['level'] = $level;
                $v['son'] = in_array($v['id'], $pid_arr);// 如果自身id出现在$pid_arr集合中，证明有子元素
                $list[] = $v;
                static::arrayToLevel($array, $field, $v['id'], $level + 1, $list);
            }
        }
        return $list;
    }

    /**
     * 把返回的数据集转换成Tree，子集放入父级中
     * @param array $list 要转换的数据集
     * @param string $pk 主键唯一标识
     * @param string $pid parent标记字段
     * @param string $child 子集的键名
     * @param int $root 层级
     * @return array
     */
    public static function arrayToTree(array $list, string $pk = 'id', string $pid = 'pid', string $child = '_child', int $root = 0): array
    {
        // 创建Tree
        $tree = [];
        // 创建基于主键的数组引用
        $refer = [];
        foreach ($list as $key => $data) {
            $refer[$data[$pk]] = &$list[$key];
        }
        foreach ($list as $key => $data) {
            // 判断是否存在parent
            $parentId = $data[$pid];
            if ($root == $parentId) {
                $tree[] = &$list[$key];
            } else {
                if (isset($refer[$parentId])) {
                    $parent = &$refer[$parentId];
                    $parent[$child][] = &$list[$key];
                }
            }
        }
        return $tree;
    }

    /**
     * 根据键给二维数组分组
     * @param array $array 二维数组
     * @param string|int $key 键
     * @return array    返回分组后数组
     */
    public static function arrayGroupBy(array $array, $key): array
    {
        $grouped = [];
        foreach ($array as $value) {
            $grouped[$value[$key]][] = $value;
        }
        if (func_num_args() > 2) {
            $args = func_get_args();
            foreach ($grouped as $key => $value) {
                $params = array_merge([$value], array_slice($args, 2, func_num_args()));
                $grouped[$key] = call_user_func_array('static::arrayGroupBy', $params);
            }
        }
        return $grouped;
    }

    /**
     * 二维数组根据多个键排序
     * @param mixed $array 二维数组
     * @param string $order
     */
    public static function arrayToSort(&$array, string $order = 'sort desc'): void
    {
        if (empty($array)) {
            $array = '';
        }
        if (is_array($array)) {
            $param = [];
            $sort = ['desc' => SORT_DESC, 'asc' => SORT_ASC];
            foreach (array_map('trim', explode(',', $order)) as $val) {
                $key = array_map('trim', explode(' ', $val));
                $param[] = array_column($array, $key[0]);
                $param[] = $sort[$key[1]];
            }
            $param[] = &$array;
            array_multisort(...$param);
        }
    }

    /**
     * 二维数组排列组合
     * @param array $options
     * @return array
     */
    public static function combination(array $options): array
    {
        $rows = [];
        foreach ($options as $option => $items) {
            if (!empty($rows)) {
                // 2、将第一列作为模板
                $clone = $rows;
                // 3、置空当前列表，因为只有第一列的数据，组合是不完整的
                $rows = [];
                // 4、遍历当前列，追加到模板中，使模板中的组合变得完整
                foreach ($items as $item) {
                    $tmp = $clone;
                    foreach ($tmp as $index => $value) {
                        $value[$option] = $item;
                        $tmp[$index] = $value;
                    }
                    // 5、将完整的组合拼回原列表中
                    $rows = array_merge($rows, $tmp);
                }
            } else {
                // 1、先计算出第一列
                foreach ($items as $item) {
                    $rows[][$option] = $item;
                }
            }
        }
        return $rows;
    }

    /**
     * 不区分大小写的in_array实现
     * @param mixed $value 待搜索的值。
     * @param array $array 待搜索的数组。
     * @return boolean  如果找到则返回 TRUE，否则返回 FALSE
     */
    public static function inArrayCI($value, array $array): bool
    {
        return in_array(strtolower($value), array_map('strtolower', $array));
    }
}